1 // Level Format: A basic API for 2D tile-based games
2 // Copyright (C) 2019  TheOnlyMrCat
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Lesser General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU Lesser General Public License
15 // along with this program.  If not, see <https://www.gnu.org/licenses/>.
16 //
17 
18 module mrcat.lvfmt.reader;
19 
20 import std.array : appender;
21 import std.exception;
22 import std.stdio;
23 
24 import mrcat.lvfmt.object;
25 import mrcat.lvfmt.level;
26 
27 Level!T readLevel(T : BaseObject)(inout string filename) {
28     File file = File(filename);
29     auto l = new Level!T();
30 
31     const int width  = file.rawRead(new int[1])[0];
32     const int height = file.rawRead(new int[1])[0];
33     const byte bitWidth = file.rawRead(new byte[1])[0];
34     if (bitWidth % 8 != 0) throw new Exception("Invalid bit width");
35 
36     file.rawRead(new byte[3]); // Reserved header space
37 
38     const byte bytes = bitWidth / 8;
39 
40     l.map = new long[][height];
41 
42     for (int y = 0; y < height; y++) {
43         l.map[y] = new long[width];
44         for (int x = 0; x < width; x++) {
45             byte[] buf = new byte[bytes];
46             file.rawRead(buf);
47 
48             l.map[y][x] = 0;
49             for (int i = 0; i < bytes; i++) {
50                 l.map[y][x] += buf[i] << (bytes - i - 1) * 8;
51             }
52         }
53     }
54 
55     auto objects = appender!(T[])();
56 
57     // File pointer operations to check for EOF
58     FILE* fp = file.getFP();
59     int c = getc(fp);
60 
61     while (!file.eof()) {
62         ungetc(c, fp);
63         T obj = new T;
64         obj.deserialize(file);
65         objects.put(obj);
66         c = getc(fp);
67     }
68 
69     l.objects = objects[];
70 
71     return l;
72 }
73 
74 version(unittest) {
75     class TestObject : BaseObject {
76         override void deserialize(File f) {
77             lVal = f.rawRead(new long[1])[0];
78         }
79 
80         override void serialize(File f) {
81             f.rawWrite([lVal]);
82         }
83 
84         long lVal;
85     }
86 }
87 
88 unittest {
89     auto l = readLevel!TestObject("test/map.lft");
90     assert(l.map.length == 0x10);
91     assert(l.map[0].length == 0x10);
92     assert(l.map[4][2] == 0x05);
93     assert(l.objects[0].lVal == 0x10);
94     assert(l.objects[1].lVal == 0x1000);
95 }